home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1998 January
/
Macworld (1998-01).dmg
/
Shareware World
/
Comms & Internet
/
HTML mode 2.0 etc.
/
hctsmsl.tcl
< prev
next >
Wrap
Text File
|
1997-09-22
|
34KB
|
1,026 lines
## -*-Tcl-*-
# ###################################################################
# HTML and CSS mode - tools for editing Cascading Style Sheets
#
# FILE: "hctsmsl.tcl"
# created: 97-03-08 19.32.58
# last update: 97-09-21 19.12.09
# Author: Johan Linde
# E-mail: <jl@theophys.kth.se>
# www: <http://bach.theophys.kth.se/~jl/Alpha.html>
#
# Version: 2.0 and 1.0
#
# Copyright 1996, 1997 by Johan Linde
#
# This software may be used freely, and distributed freely, as long as the
# receiver is not obligated in any way by receiving it.
#
# If you make improvements to this file, please share them!
#
# ###################################################################
##
proc hctsmsl.tcl {} {}
# Units allowed for length.
set cssUnits {em ex px pt cm mm in pc}
# These properties can take a number as value.
set cssNumbers {line-height}
# These properties can take length values.
set cssLengths {font-size line-height background-position word-spacing letter-spacing
text-indent margin-top margin-right margin-bottom margin-left padding-top padding-right
padding-bottom padding-left border-top-width border-right-width border-bottom-width
border-left-width border-width width height}
# These properties can take percentage values.
set cssPercentage {font-size line-height background-position vertical-align text-indent
margin-top margin-right margin-bottom margin-left padding-top padding-right
padding-bottom padding-left width}
# These properties can take URL values.
set cssURLs {background-image list-style-image @import}
# These properties can take color values.
set cssColors {color background-color border-color}
# These properties can take any value.
set cssAny {font-family}
# Groups of properties for different dialogs.
set cssGroup(font) {font-style font-variant font-weight font-size line-height font-family}
set cssGroup(background) {background-color background-image background-repeat
background-attachment background-position}
set cssGroup(text) {word-spacing letter-spacing text-decoration vertical-align
text-transform text-align text-indent}
set cssGroup(margin) {margin-top margin-right margin-bottom margin-left}
set cssGroup(padding) {padding-top padding-right padding-bottom padding-left}
set cssGroup(border) {border-width border-style border-color}
set cssGroup(border-width) {border-top-width border-right-width border-bottom-width
border-left-width}
set cssGroup(size) {width height}
set cssGroup(Display) {display white-space}
set cssGroup(list-style) {list-style-type list-style-image list-style-position}
# These of the above groups are shorthands.
set cssShorthands {font background margin padding border border-width list-style}
# Possible values of the css properties.
set cssProperty(font-family) {serif sans-serif cursive fantasy monospace}
set cssProperty(font-style) {italic oblique normal}
set cssProperty(font-variant) {small-caps normal}
set cssProperty(font-weight) {bold bolder lighter 100 200 300 400 500 600 700 800 900 normal}
set cssProperty(font-size) {larger smaller xx-small x-small small medium large x-large xx-large}
set cssProperty(line-height) {normal}
set cssProperty(background-color) {transparent}
set cssProperty(background-image) {none}
set cssProperty(background-repeat) {repeat-x repeat-y no-repeat repeat}
set cssProperty(background-attachment) {fixed scroll}
set cssProperty(background-position) {{top center bottom} {left center right}}
set cssProperty(word-spacing) {normal}
set cssProperty(letter-spacing) {normal}
set cssProperty(text-decoration) {none {underline overline line-through blink}}
set cssProperty(vertical-align) {sub super top text-top middle bottom text-bottom baseline}
set cssProperty(text-transform) {capitalize uppercase lowercase none}
set cssProperty(text-align) {left right center justify}
set cssProperty(margin-top) {auto}
set cssProperty(margin-right) {auto}
set cssProperty(margin-bottom) {auto}
set cssProperty(margin-left) {auto}
set cssProperty(border-width) {thin medium thick}
set cssProperty(border-top-width) {thin medium thick}
set cssProperty(border-right-width) {thin medium thick}
set cssProperty(border-bottom-width) {thin medium thick}
set cssProperty(border-left-width) {thin medium thick}
set cssProperty(border-style) {dotted dashed solid double groove ridge inset outset none}
set cssProperty(width) {auto}
set cssProperty(height) {auto}
set cssProperty(float) {left right none}
set cssProperty(clear) {left right both none}
set cssProperty(display) {block inline list-item none}
set cssProperty(white-space) {pre nowrap normal}
set cssProperty(list-style-type) {disc circle square decimal lower-roman upper-roman lower-alpha
upper-alpha none}
set cssProperty(list-style-image) {none}
set cssProperty(list-style-position) {inside outside}
proc cssGetHtmlWords {} {
global cssHtmlWords htmlElemAttrOptional1 htmlElemAttrOptional3 HTMLmodeVars htmlModeIsLoaded
if {![info exists htmlModeIsLoaded]} {
return $cssHtmlWords
} else {
catch {unset cssHtmlWords}
return [array names htmlElemAttrOptional[set HTMLmodeVars(htmlPackageToUse)]]
}
}
# ◊◊◊◊ Change below for new system §19 ◊◊◊◊ #
# Word completion
proc cssWordComplete {} {
global cssLengths cssPercentage cssColors cssURLs cssAny cssGroup cssProperty
global HTMLmodeVars
set allCss [removeDups [concat $cssLengths $cssPercentage $cssColors $cssURLs $cssAny \
[array names cssGroup] [array names cssProperty] border-left border-top border-bottom border-right]]
foreach p {size text Display} {
set allCss [lreplace $allCss [set w [lsearch $allCss $p]] $w]
}
set matches ""
# Between {}?
set thepos [getPos]
if {$thepos == [maxPos]} {set thepos [expr [maxPos] - 1]}
if {[catch {matchIt "\}" $thepos} bpos]} {
set allHtmlWords [cssGetHtmlWords]
set pos [getPos]
backwardWord
set word [string toupper [getText [getPos] $pos]]
foreach p $allHtmlWords {
if {[string match $word* $p]} {lappend matches $p}
}
if {![llength $matches]} {
select [getPos] $pos
} else {
replaceText [getPos] $pos [largestPrefix $matches]
}
return
}
# Get current word
if {[catch {search -s -f 0 -m 0 -r 1 {[\{;: \t\r]} [expr [getPos] - 1]} wpos]} {set wpos "0 0"}
set wpos [lindex $wpos 1]
set word [getText $wpos [getPos]]
# Before or after :?
if {[catch {search -s -f 0 -m 0 -r 0 {;} [expr [getPos] - 1]} spos] || [lindex $spos 0] < $bpos} {set spos 0}
set spos [lindex $spos 0]
if {[catch {search -s -f 0 -m 0 -r 0 {:} [getPos]} cpos] || [lindex $cpos 0] < $bpos} {set cpos 0}
set cpos [lindex $cpos 0]
if {$spos < $cpos} {
# After colon
if {[catch {search -s -f 0 -m 0 -r 1 {[; \t\r]} $cpos} w2pos]} {set w2pos 0}
set pword [getText [lindex $w2pos 1] $cpos]
if {[lsearch -exact $cssURLs $pword] >= 0 || [string match "url(*" $word]} {
set matchWords $HTMLmodeVars(URLs)
incr wpos 4
set word [string trimleft [string range $word 4 end] \"]
set isURL 1
} else {
set matchWords [eval concat $cssProperty($pword)]
set isURL 0
}
foreach p $matchWords {
if {[string match $word* $p]} {lappend matches $p}
}
if {![llength $matches]} {
select $wpos [getPos]
} else {
replaceText $wpos [getPos] [lindex {"" "\""} $isURL][largestPrefix $matches]
}
} else {
# Before colon
foreach p $allCss {
if {[string match $word* $p]} {lappend matches $p}
}
if {![llength $matches]} {
select $wpos [getPos]
} else {
set word [largestPrefix $matches]
if {[llength $matches] == 1} {
append word ": "
set backTwo 0
if {[lsearch -exact $cssURLs [string trimright $word ": "]] >= 0} {
append word "url(\"\")"
set backTwo 1
}
}
replaceText $wpos [getPos] $word
if {$backTwo} {goto [expr [getPos] - 2]}
}
}
}
# ◊◊◊◊ end changing for new system §19 ◊◊◊◊ #
# CSS properties dialog.
proc cssDialog {group} {
global cssGroup cssProperty cssAny cssURLs cssLengths cssPercentage cssColors cssUnits
global htmluserColors htmlColorName basicColors HTMLmodeVars cssShorthands mode cssNumbers
if {$mode == "HTML" && ![htmlIsInContainer STYLE]} {
beep
message "Current position is not inside STYLE tags."
return
}
# Fins where to insert text.
set gpos [getPos]
if {$gpos > 0} {incr gpos -1}
if {[catch {search -s -f 0 -m 0 -r 1 "\{" $gpos} lbrace]} {set lbrace 0; set noleft 1}
set lbrace [expr [lindex $lbrace 0] + 1]
if {[catch {search -s -f 0 -m 0 -r 1 "\}" $gpos} rbrace]} {set rbrace 0}
set rbrace [expr [lindex $rbrace 0] + 1]
if {([info exists noleft] || $rbrace > $lbrace) && $group != "@import"} {alertnote "Incorrect position to insert properties."; return}
if {[catch {search -s -f 0 -m 0 -r 1 "\;" $gpos} semi] || [lindex $semi 0] < $lbrace} {set semi 0}
set semi [expr [lindex $semi 0] + 1]
if {$group != "@import" && ($lbrace > 1 || $semi > 1)} {goto [expr $lbrace > $semi ? $lbrace : $semi]}
# define colors
set htmlColors [lsort [array names htmluserColors]]
append htmlColors " - " $basicColors
# urls
set URLs $HTMLmodeVars(URLs)
# these fit in half the size of the dialog window
set halfIsEnough {font-style font-variant font-weight text-transform text-align white-space}
# These needs more space
set dw 0
if {$group == "background" || $group == "border-width" || $group == "list-style"} {set dw 40}
# obtain all props for this group
if {[info exists cssGroup($group)]} {
set props $cssGroup($group)
} else {
set props $group
}
# build the dialog
set invalidInput 1
set short 1
set allvalues 0
set val [cssGetProperties $group]
if {[info exists errorText] && ![htmlErrorWindow "$group not well-defined" $errorText 1]} {return}
while {$invalidInput} {
while {1} {
if {$group == "@import"} {
set htxt "Import Style Sheet"
} else {
set htxt "[string toupper [string index $group 0]][string range $group 1 end] properties"
}
set box "-t [list $htxt] 120 10 450 25"
set fileIndex ""
set colorIndex ""
set proptypes ""
set hpos 35
set ind 2
set wpos 10
foreach p $props {
if {[lsearch -exact $halfIsEnough $p] < 0 || $wpos > 235} {
if {$wpos > 10} {set wpos 10; incr hpos 30}
}
if {$p != "@import"} {lappend box -t ${p}: $wpos $hpos [expr $wpos + 110 + $dw] [expr $hpos + 15]}
incr wpos 120
incr wpos $dw
if {[info exists cssProperty($p)]} {
# A list of choices
set pr $cssProperty($p)
# special case with background-position and text-decoration
if {$p == "background-position" || $p == "text-decoration"} {
set pr1 [lindex $pr 0]
if {[llength $pr1] > 1} {
lappend box -m [concat [list [lindex $val $ind] "No value"] $pr1] \
$wpos $hpos [expr $wpos + 95] [expr $hpos + 15]
} else {
lappend box -c $pr1 [lindex $val $ind] $wpos $hpos [expr $wpos + 95] [expr $hpos + 15]
}
incr wpos 105
incr ind
set pr [lindex $pr 1]
lappend proptypes $p choices
}
set n 1
# four times for text-decoration and border-style
if {$p == "text-decoration" || $group == "border-style"} {set n 4}
for {set i 0} {$i < $n} {incr i} {
if {$wpos > 355 + $dw} {
set wpos [expr 130 + $dw]
incr hpos 30
}
if {[llength $pr] > 1} {
lappend box -m [concat [list [lindex $val $ind] "No value"] $pr] \
$wpos $hpos [expr $wpos + 95] [expr $hpos + 15]
} else {
lappend box -c $pr [lindex $val $ind] $wpos $hpos [expr $wpos + 95] [expr $hpos + 15]
}
incr wpos 105
incr ind
lappend proptypes $p choices
}
}
set l [lsearch -exact $cssLengths $p]
set pr [lsearch -exact $cssPercentage $p]
if { $l >= 0 || $pr >= 0 } {
# Length or percentage
set n 1
# twice for background-position
if {$p == "background-position"} {set n 2}
for {set i 0} {$i < $n} {incr i} {
if {$wpos > 335 + $dw} {
set wpos [expr 130 + $dw]
incr hpos 30
}
set units ""
if {$l >= 0} {set units $cssUnits}
if {$pr >= 0} {lappend units %}
set uw 110
if {[lsearch -exact $cssNumbers $p] >= 0} {set units "{No unit} $units"; set uw 160}
lappend box -e [lindex $val $ind] $wpos $hpos [expr $wpos + 50] [expr $hpos + 15]
lappend box -m [concat [list [lindex $val [expr $ind + 1]]] $units] \
[expr $wpos + 60] $hpos [expr $wpos + $uw] [expr $hpos + 15]
incr wpos 120
incr ind 2
lappend proptypes $p number
}
set wpos 10
incr hpos 30
}
if {[lsearch -exact $cssAny $p] >= 0} {
# Any value
if {$wpos > 10} {set wpos 10; incr hpos 30}
lappend box -e [lindex $val $ind] 10 $hpos 450 [expr $hpos + 15]
incr ind
set wpos 10
incr hpos 30
lappend proptypes $p any
}
if {[lsearch -exact $cssColors $p] >=0 } {
# color
set n 1
# four times for border-color
if {$group == "border-color"} {set n 4}
for {set i 0} {$i < $n} {incr i} {
if {$wpos > 130} {set wpos 10; incr hpos 30}
lappend box -e [lindex $val $ind] 130 $hpos 200 [expr $hpos + 15] \
-m [concat [list [lindex $val [expr $ind + 1]] {No value}] $htmlColors] \
210 $hpos 340 [expr $hpos + 15] \
-b "New Color…" 350 $hpos 450 [expr $hpos + 20]
incr ind 3
lappend colorIndex [expr $ind - 1]
set wpos 10
incr hpos 40
lappend proptypes $p color
}
}
if {[lsearch -exact $cssURLs $p] >= 0} {
# URL
if {$wpos > 130} {set wpos 10; incr hpos 30}
lappend box -e [lindex $val $ind] 120 $hpos 450 [expr $hpos + 15] \
-m [concat [list [lindex $val [expr $ind + 1]] {No value}] $URLs] \
120 [expr $hpos + 25] 450 [expr $hpos + 35] \
-b "File…" 10 [expr $hpos + 20] 70 [expr $hpos + 40]
incr ind 3
lappend fileIndex [expr $ind - 1]
set wpos 10
incr hpos 50
lappend proptypes $p url
}
if {[string match "*left*" $p]} {
if {$wpos > 130} {set wpos 10; incr hpos 30}
lappend box -r "Set all values individually" $allvalues 10 $hpos 300 [expr $hpos + 15]
lappend box -r "Add missing values automatically if possible" [expr !$allvalues] 10 [expr $hpos + 20] 350 [expr $hpos + 35]
set allValIndex $ind
incr ind 2
set wpos 10
incr hpos 40
lappend proptypes $p allval
}
}
if {$wpos > 10} {incr hpos 20}
if {[lsearch -exact $cssShorthands $group] >= 0} {
lappend box -c "Use shorthand form if possible" $short 10 $hpos 250 [expr $hpos + 15]
incr hpos 20
set shortIndex $ind
}
set val [eval [concat dialog -w [expr 460 + $dw] -h [expr $hpos + 50] \
-b OK 20 [expr $hpos + 20] 85 [expr $hpos + 40] \
-b Cancel 110 [expr $hpos + 20] 175 [expr $hpos + 40] $box]]
if {[info exists shortIndex]} {set short [lindex $val $shortIndex]}
if {[info exists allValIndex]} {set allvalues [lindex $val $allValIndex]}
# OK clicked?
if {[lindex $val 0]} {break}
# Cancel clicked?
if {[lindex $val 1]} {return}
# File button clicked?
foreach fl $fileIndex {
if {[lindex $val $fl] && [set newFile [htmlGetFile]] != ""} {
set URLs $HTMLmodeVars(URLs)
set val [lreplace $val [expr $fl - 1] [expr $fl - 1] [lindex $newFile 0]]
}
}
# Color button clicked?
foreach cl $colorIndex {
if {[lindex $val $cl] && [set newColor [htmlAddNewColor]] != ""} {
set htmlColors [concat [list $newColor] $htmlColors]
set val [lreplace $val [expr $cl -1] [expr $cl - 1] "$newColor"]
}
}
}
# Find indentation.
set indent ""
if {![catch {matchIt "\}" [getPos]} pos]} {
regexp {^[ \t]*} [getText [lineStart $pos] $pos] indent
}
# Put it all together.
set j 2
set prevprop ""
set proptext ""
set errtext ""
set tmptext ""
for {set i 0} {$i < [llength $proptypes]} {incr i 2} {
set prop [lindex $proptypes [expr $i + 1]]
if {$prevprop != [set pr [lindex $proptypes $i]]} {
if {$tmptext != ""} {
if {$prevprop == "text-decoration"} {
if {[lindex $tmptext 0] == "1"} {
set tmptext " none"
} elseif {$tmptext != " 0"} {
set tmptext " [removeDups [lrange $tmptext 1 end]]"
}
} else {
set tmptext " [lindex $tmptext 0]"
}
if {$tmptext != " 0"} {
if {[info exists important($prevprop)] || [info exists important($group)]} {append tmptext " ! important"}
append proptext "\;\r$indent\t$prevprop:$tmptext"
}
}
set prevprop $pr
set tmptext ""
}
switch $prop {
choices {
if {[llength $cssProperty($pr)] == 1} {
if {[lindex $val $j]} {
append tmptext " $cssProperty($pr)"
}
} elseif {[set c [lindex $val $j]] != "No value"} {
append tmptext " $c"
}
incr j
}
number {
if {[set c [string trim [lindex $val $j]]] != ""} {
if {![catch {cssCheckNumber $pr $c [lindex $val [expr $j + 1]]} c]} {
append tmptext " $c"
} else {
lappend errtext "$pr: $c"
}
}
incr j 2
}
any {
if {[set c [string trim [lindex $val $j]]] != ""} {
append tmptext ", $c"
}
incr j
}
color {
if {[set ctxt [string trim [lindex $val $j]]] != ""} {
if {[set col [cssCheckColorNumber $ctxt]] == 0} {
lappend errtext "$pr: $ctxt is not a valid color number."
} else {
append tmptext " $col"
}
} elseif {[set cval [lindex $val [expr $j + 1]]] != "No value"} {
if {[info exists htmluserColors($cval)]} {
append tmptext " $htmluserColors($cval)"
}
if {[info exists htmlColorName($cval)]} {
append tmptext " $htmlColorName($cval)"
}
}
incr j 3
}
url {
if {[set turl [string trim [lindex $val $j]]] != ""} {
append tmptext " url(\"[htmlURLescape2 $turl]\")"
htmlAddToCache URLs $turl
} elseif {[set murl [lindex $val [expr $j + 1]]] != "No value"} {
append tmptext " url(\"[htmlURLescape2 $murl]\")"
}
incr j 3
}
allval {
incr j 2
}
}
}
if {$tmptext != ""} {
if {$prevprop == "background-position"} {
if {[regexp {^[a-z]+$} [lindex $tmptext 0]]} {
set tp ""
foreach tm $tmptext {
if {[regexp {^[a-z]+$} $tm]} {
lappend tp $tm
}
}
set tmptext " $tp"
}
} elseif {$prevprop == "font-family"} {
set tmptext [string trim $tmptext ,]
if {[lsearch -exact $cssProperty(font-family) [set first [string trim [lindex $tmptext 0] ,]]] >= 0
&& [llength $tmptext] > 1} {
set tmptext " [lrange $tmptext 1 end], $first"
}
} elseif {$prevprop != "border-style" && $prevprop != "border-color"} {
set tmptext " [lindex $tmptext 0]"
}
if {[info exists important($prevprop)] || [info exists important($group)]} {append tmptext " ! important"}
append proptext "\;\r$indent\t$pr:$tmptext"
}
set proptext [string trimleft $proptext "\;"]
if {![llength $errtext]} {
set invalidInput 0
if {[info exists allValIndex] && !$allvalues} {set proptext [cssAddMissingValues $group $proptext $indent]}
if {[info exists shortIndex] && $short} {set proptext [cssMakeShort $group $proptext $indent]}
} else {
htmlErrorWindow "Invalid input" $errtext
}
}
# Special fixes for @import
if {$group == "@import"} {
regexp {^[ \t]*} [getText [lineStart [getPos]] [getPos]] indent
set proptext [string trimleft $proptext ";"]
regsub "\t+" $proptext "$indent" proptext
regsub "@import:" $proptext "@import" proptext
}
set len 0
if {$proptext != ""} {
set ps [getPos]
insertText "$proptext\;"
set len [expr [getPos] - $ps]
}
set removePos0 [lsort -integer -decreasing $removePos0]
set removePos1 [lsort -integer -decreasing $removePos1]
# Check for overlapping positions.
set r0 [maxPos]
for {set i 0} {$i < [llength $removePos1]} {incr i} {
set r00 [lindex $removePos0 $i]
set r11 [lindex $removePos1 $i]
if {$r11 > $r0} {set r11 $r0}
if {$r11 > $r00} {lappend rem [list $r00 $r11]}
set r0 $r00
}
foreach r $rem {
set xpos 0
if {[set pos1 [lindex $r 0]] >= $ps} {set xpos $len}
deleteText [expr $pos1 + $xpos] [expr [lindex $r 1] + $xpos]
}
}
# Add missing values to top, right, bottom, left properties.
proc cssAddMissingValues {group text indent} {
global cssGroup
set tmp [split $text "\r"]
set sideList {top right bottom left}
# Find those values which have been set
foreach side $sideList {
set $side 0
foreach l $tmp {
if {[string match *${side}* [lindex $l 0]]} {
set $side 1
set ${side}val [string trimright [lindex $l 1] "\;"]
}
}
}
# Add missing values.
foreach side $sideList {
if {![set $side]} {
switch $side {
top {set opside bottom}
right {set opside left}
bottom {set opside top}
left {set opside right}
}
if {[set $opside]} {
set use $opside
} elseif {$top} {
set use top
} else {
# Can't add missing value.
return $text
}
append text "\;\r$indent\t[lindex $cssGroup($group) [lsearch $sideList $side]]: [set ${use}val]"
}
}
return $text
}
# Makes a short form of a group of properties.
proc cssMakeShort {group text indent} {
global cssGroup
set lines [split $text \r]
set count 0
set important 0
foreach pr $cssGroup($group) {
foreach l $lines {
if {[lindex $l 0] == "$pr:"} {
incr important [regsub { ! important} $l {} l]
incr count
if {$pr == "font-size"} {set fontSize 1}
if {$pr == "font-family"} {set fontFamily 1}
# Line-height is a special case.
if {$pr == "line-height" && [info exists fontSize]} {
append values /[string trimright [lrange $l 1 end] "\;"]
} else {
append values " " [string trimright [lrange $l 1 end] "\;"]
}
}
}
}
if {$important > 0 && $important != $count} {return $text}
# font-size and font-family must be used for font.
if {$group == "font" && (![info exists fontSize] || ![info exists fontFamily])} {return $text}
# Remove unnecessary stuff for margin and padding and border-width.
if {$group == "margin" || $group == "padding" || $group == "border-width"} {
# If count ≠ 4, then there is no short form
if {$count != 4} {return $text}
if {[llength [removeDups $values]] == 1} {
set values " [lindex $values 0]"
} elseif {[lindex $values 0] == [lindex $values 2] && [lindex $values 1] == [lindex $values 3]} {
set values [lrange $values 0 1]
} elseif {[lindex $values 1] == [lindex $values 3]} {
set values [lrange $values 0 2]
}
}
set text ""
if {[lindex $lines 0] == "\;"} {set text "\;"}
if {[info exists values]} {
if {$group == "font"} {set values " [removeDups $values]"}
append text "\r$indent\t$group:$values"
if {$important} {append text " ! important"}
}
return $text
}
# Check if a CSS number is ok.
proc cssCheckNumber {prop num unit} {
global cssPercentage cssLengths cssUnits
if {![regexp {^(-?[0-9]+\.?[0-9]*)([%a-z]*)$} $num d n u]} {
error "Invalid number."
}
if {$u != ""} {
if {[lsearch -exact [concat $cssUnits %] $u] < 0 ||
$u != "%" && [lsearch -exact $cssLengths $prop] < 0} {
error "Invalid unit."
} else {
set unit $u
}
} elseif {$unit == "No unit"} {
set unit ""
}
if {$unit == "%" && [lsearch -exact $cssPercentage $prop] < 0} {
error "Percentage not allowed."
}
return "$n$unit"
}
# Check if a color number is a valid number, or one of the predefined names.
# Returns 0 if not and the color number if it is.
proc cssCheckColorNumber {color} {
global htmlColorName
set color [string tolower $color]
if {[info exists htmlColorName($color)]} {return $htmlColorName($color)}
# rgb(1,2,3)
if {[regexp {^rgb\(([0-9]+),([0-9]+),([0-9]+)\)$} $color dum c1 c2 c3]} {
if {$c1 > -1 && $c1 < 256 && $c2 > -1 && $c2 < 256 && $c3 > -1 && $c3 < 256} {
return $color
} else {
return 0
}
}
# rgb(1.0%,2.0%,3.0%)
if {[regexp {^rgb\(([0-9]+\.?[0-9]*)%,([0-9]+\.?[0-9]*)%,([0-9]+\.?[0-9]*)%\)$} $color dum c1 c2 c3]} {
if {$c1 >= 0.0 && $c1 <= 100.0 && $c2 >= 0.0 && $c2 <= 100.0 && $c3 >= 0.0 && $c3 <= 100.0} {
return $color
} else {
return 0
}
}
# #123456 or #123
if {[string index $color 0] != "#"} {
set color "#${color}"
}
set color [string toupper $color]
if {([string length $color] != 7 && [string length $color] != 4) || ![regexp {^#[0-9A-F]+$} $color]} {
return 0
} else {
return $color
}
}
# Extracts the current values for the property to add.
proc cssGetProperties {group} {
global cssGroup cssProperty cssAny cssURLs cssLengths cssPercentage cssColors
global htmluserColorname htmlColorNumber HTMLmodeVars cssShorthands
upvar removePos0 remove0 removePos1 remove1 important important
upvar short short errorText errorText
if {$group == "@import"} {return}
# obtain all props for this group
if {[info exists cssGroup($group)]} {
set props $cssGroup($group)
} else {
set props $group
}
# Find interval to search in.
if {[catch {matchIt "\}" [getPos]} start]} {
if {![catch {search -s -f 0 -m 0 -r 0 "\}" [getPos]} r0] ||
![catch {search -s -f 1 -i 1 -m 0 -r 0 "<STYLE([ \t\r]+[^<>]*>|>)" [getPos]} r0]} {
set start [lindex $r0 1]
} else {
set start 0
}
}
if {[catch {matchIt "\{" [getPos]} end]} {
set rbrace [maxPos]
set style [maxPos]
if {![catch {search -s -f 1 -m 0 -r 0 "\{" [getPos]} r0]} {
set rbrace [lineStart [lindex $r0 0]]
}
if {![catch {search -s -f 1 -i 1 -m 0 -r 0 "</STYLE>" [getPos]} r0]} {
set style [lindex $r0 0]
}
set end [expr $rbrace < $style ? $rbrace : $style]
}
# build a list with property values
set val {0 0}
set remove ""
# Find shorthand property
if {[lsearch -exact $cssShorthands $group] >= 0} {
if {[catch {search -s -f 1 -i 1 -m 0 -r 1 -l $end "\[ \t\r;\]+$group\[ \t\r\]*:" $start} res]} {
set groupValue ""
} elseif {![catch {search -s -f 1 -i 1 -m 0 -r 0 -l $end "\;" [lindex $res 1]} res1]} {
set groupValue [string trim [getText [lindex $res 1] [expr [lindex $res1 1] - 1]]]
lappend remove0 [lindex $res 0]
lappend remove1 [lindex $res1 1]
} else {
set groupValue [string trim [getText [lindex $res 1] [expr $end - 1]]]
lappend remove0 [lindex $res 0]
lappend remove1 $end
}
set groupValue [string tolower $groupValue]
regsub -all {/\*[^\*]*\*/} $groupValue "" groupValue
if {[regsub {![ \t\r]*important} $groupValue {} groupValue]} {set important($group) 1}
if {$groupValue != ""} {
cssExpandProps $group $groupValue
}
}
foreach p $props {
# Find the property
if {[catch {search -s -f 1 -i 1 -m 0 -r 1 -l $end "\[ \t\r;\]+$p\[ \t\r\]*:" $start} res]} {
if {![info exists propValue($p)]} {set propValue($p) ""}
} elseif {![catch {search -s -f 1 -i 1 -m 0 -r 0 -l $end "\;" [lindex $res 1]} res1]} {
set propValue($p) [string trim [getText [lindex $res 1] [expr [lindex $res1 1] - 1]]]
lappend remove0 [lindex $res 0]
lappend remove1 [lindex $res1 1]
set short 0
} else {
set propValue($p) [string trim [getText [lindex $res 1] [expr $end - 1]]]
lappend remove0 [lindex $res 0]
lappend remove1 $end
set short 0
}
regsub -all {/\*[^\*]*\*/} $propValue($p) "" propValue($p)
}
foreach p $props {
set thisValue [string tolower $propValue($p)]
if {[regsub {![ \t\r]*important} $thisValue {} thisValue]} {set important($p) 1}
if {[info exists cssProperty($p)]} {
# A list of choices
set pr $cssProperty($p)
# special case with background-position and text-decoration
if {$p == "background-position" || $p == "text-decoration"} {
set pr1 [lindex $pr 0]
if {[llength $pr1] > 1} {
set found 0
for {set i 0} {$i < [llength $thisValue]} {incr i} {
set tv [lindex $thisValue $i]
if {[lsearch -exact $pr1 $tv] >= 0} {
lappend val [lindex $thisValue $i]
set thisValue [lreplace $thisValue $i $i]
set found 1
break
}
}
if {!$found} {lappend val "No value"}
} elseif {[set ww [lsearch -exact $thisValue $pr1]] >= 0} {
set thisValue [lreplace $thisValue $ww $ww]
lappend val 1
} else {
lappend val 0
}
set pr [lindex $pr 1]
}
set n 1
# four times for text-decoration and border-style
if {$p == "text-decoration" || $group == "border-style"} {set n 4}
for {set i 0} {$i < $n} {incr i} {
if {[llength $pr] > 1} {
if {[llength $thisValue] && [lsearch -exact $pr [lindex $thisValue 0]] >= 0} {
lappend val [lindex $thisValue 0]
set thisValue [lrange $thisValue 1 end]
} else {
lappend val "No value"
}
} elseif {$thisValue == $pr} {
lappend val 1
set thisValue ""
} else {
lappend val 0
}
}
}
set l [lsearch -exact $cssLengths $p]
set pr [lsearch -exact $cssPercentage $p]
if { $l >= 0 || $pr >= 0 } {
# Length or percentage
set n 1
# twice for background-position
if {$p == "background-position"} {set n 2}
for {set i 0} {$i < $n} {incr i} {
if {$i < [llength $thisValue] && ![catch {cssCheckNumber $p [lindex $thisValue 0] ""} num]} {
regexp {[0-9]+(.*)} $num dum unit
lappend val $num $unit
set thisValue [lrange $thisValue 1 end]
} else {
lappend val "" ""
}
}
}
if {[lsearch -exact $cssAny $p] >= 0} {
# Any value
lappend val $thisValue
set thisValue ""
}
if {[lsearch -exact $cssColors $p] >=0 } {
# color
set n 1
# four times for border-color
if {$group == "border-color"} {set n 4}
for {set i 0} {$i < $n} {incr i} {
set tv [cssCheckColorNumber [lindex $thisValue 0]]
if {$tv == "0"} {
lappend val "" "No value" 0
} elseif {[info exists htmluserColorname($tv)]} {
lappend val "" $htmluserColorname($tv) 0
} elseif {[info exists htmlColorNumber($tv)]} {
lappend val "" $htmlColorNumber($tv) 0
} else {
lappend val $tv "No value" 0
}
if {$tv != "0"} {set thisValue [lrange $thisValue 1 end]}
}
}
if {[lsearch -exact $cssURLs $p] >= 0} {
# URL
if {[regexp {url\(\"?([^\"\)]+)\"?\)} $thisValue dum thisValue]} {
set thisValue [htmlURLunEscape $thisValue]
htmlAddToCache URLs $thisValue
lappend val "" $thisValue 0
set thisValue ""
} else {
lappend val "" "No value" 0
}
}
if {[llength $thisValue]} {lappend errorText "$p: $thisValue"}
}
return $val
}
proc cssExpandProps {group value} {
global cssGroup cssProperty cssAny cssURLs cssLengths cssPercentage cssColors cssUnits
upvar propValue prop errorText errorText
# Special case with font
if {$group == "font"} {
regexp {[^ \t]+(,[ \t]+[^ \t]+)*[ \t]*$} $value family
set prop(font-family) [string trim $family]
set value [string range $value 0 [expr [string length $value] - [string length $family] - 1]]
set fontsize [lindex $value [expr [llength $value] - 1]]
set lineheight ""
regexp {^([^/]+)/?(.*)$} $fontsize dum fontsize lineheight
if {[lsearch -exact $cssProperty(font-size) $fontsize] >= 0 || ![catch {cssCheckNumber font-size $fontsize ""} fontsize]} {
set prop(font-size) $fontsize
}
if {[lsearch -exact $cssProperty(line-height) $lineheight] >= 0 || ![catch {cssCheckNumber line-height $lineheight ""} lineheight]} {
set prop(line-height) $lineheight
}
set value [lrange $value 0 [expr [llength $value] - 2]]
set normal [lsearch -exact $value normal]
regsub -all "normal" $value "" value
}
# Special case with background-position
if {$group == "background"} {
foreach bp $cssProperty(background-position) {
set nv ""
foreach v $value {
if {[lsearch -exact $bp $v] >= 0} {
lappend prop(background-position) $v
} else {
lappend nv $v
}
}
set value $nv
}
set nv ""
foreach v $value {
if {![catch {cssCheckNumber background-position $v ""} v1]} {
lappend prop(background-position) $v1
} else {
lappend nv $v
}
}
set value $nv
}
# Handle margin, padding and border-width separately
if {$group == "margin" || $group == "padding" || $group == "border-width"} {
foreach trbl {top right bottom left} {
if {$group == "border-width"} {
set pr "border-${trbl}-width"
} else {
set pr ${group}-$trbl
}
set v ""
if {[llength $value]} {
set v [lindex $value 0]
set value [lrange $value 1 end]
}
if {$group != "padding" && [lsearch -exact $cssProperty($pr) $v] >= 0} {
set prop($pr) $v
} elseif {![catch {cssCheckNumber $pr $v ""} v1]} {
set prop($pr) $v1
} elseif {$v != ""} {
append err " $v"
}
}
if {[info exists err]} {lappend errorText "$group:$err"}
return
}
# All other properties.
foreach p $cssGroup($group) {
if {[info exists cssProperty($p)]} {
set p1 $cssProperty($p)
if {$group == "font" && [lsearch -exact {font-style font-weight font-variant line-height} $p] >= 0} {
set tmp ""
for {set i 0} {$i < [llength $value]} {incr i} {
set v [lindex $value $i]
if {[lsearch -exact $p1 $v] >= 0} {
set tmp $v
set value [lreplace $value $i $i]
break
}
}
if {$tmp != ""} {
set prop($p) $tmp
} elseif {$normal >= 0} {
set prop($p) normal
}
} else {
for {set i 0} {$i < [llength $value]} {incr i} {
set v [lindex $value $i]
if {[lsearch -exact $p1 $v] >= 0} {
set prop($p) $v
set value [lreplace $value $i $i]
break
}
}
}
}
if {[lsearch -exact $cssURLs $p] >= 0} {
for {set i 0} {$i < [llength $value]} {incr i} {
set v [lindex $value $i]
if {[regexp {^url\(\"?[^\"\)]+\"?\)$} $v]} {
set prop($p) $v
set value [lreplace $value $i $i]
break
}
}
}
if {[lsearch -exact $cssColors $p] >= 0} {
for {set i 0} {$i < [llength $value]} {incr i} {
set v [lindex $value $i]
if {[set c [cssCheckColorNumber $v]] != "0"} {
set prop($p) $c
set value [lreplace $value $i $i]
break
}
}
}
}
if {[llength $value]} {lappend errorText "$group: $value"}
}